﻿using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Provider.Consul;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using TestDBData.EFContext;
using TestManage.Infrastructure.RegistrarIOC;
using TestModel.Configuration;
using TestService.SysManage;

namespace TestManage.Infrastructure.Extensions
{
    /// <summary>
    /// 代表 Nop engine
    /// </summary>
    public class NopEngine : IEngine
    {
        #region 属性
        /// <summary>
        /// 服务
        /// </summary>
        private IServiceProvider _serviceProvider { get; set; }
        /// <summary>
        /// 服务
        /// </summary>
        public virtual IServiceProvider ServiceProvider => _serviceProvider;
        #endregion

        #region Utilities

        /// <summary>
        /// Get IServiceProvider
        /// </summary>
        /// <returns>IServiceProvider</returns>
        protected IServiceProvider GetServiceProvider()
        {
            var accessor = ServiceProvider.GetService<IHttpContextAccessor>();
            var context = accessor.HttpContext;
            return context?.RequestServices ?? ServiceProvider;
        }

        ///// <summary>
        ///// Run startup tasks
        ///// </summary>
        ///// <param name="typeFinder">Type finder</param>
        //protected virtual void RunStartupTasks(ITypeFinder typeFinder)
        //{
        //    //find startup tasks provided by other assemblies
        //    var startupTasks = typeFinder.FindClassesOfType<IStartupTask>();

        //    //create and sort instances of startup tasks
        //    //we startup this interface even for not installed plugins. 
        //    //otherwise, DbContext initializers won't run and a plugin installation won't work
        //    var instances = startupTasks
        //        .Select(startupTask => (IStartupTask)Activator.CreateInstance(startupTask))
        //        .OrderBy(startupTask => startupTask.Order);

        //    //execute tasks
        //    foreach (var task in instances)
        //        task.Execute();
        //}

        /// <summary>
        /// 创建服务提供者Autofac
        /// </summary>
        /// <param name="nopConfig">Startup Nop configuration parameters</param>
        /// <param name="services">Collection of service descriptors</param>
        /// <param name="typeFinder">Type finder</param>
        protected virtual IServiceProvider RegisterDependencies(IServiceCollection services)
        {
            var containerBuilder = new ContainerBuilder();
            //注册引擎
            containerBuilder.RegisterInstance(this).As<IEngine>().SingleInstance();
            //数据库
            containerBuilder.Register(context => new NopObjectContext(context.Resolve<DbContextOptions<NopObjectContext>>()))
                .As<IDbContext>().InstancePerLifetimeScope();
            //IOC
            containerBuilder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();

            containerBuilder.RegisterType<Sys_UserService>().As<ISys_UserService>().InstancePerLifetimeScope();
            containerBuilder.RegisterType<Sys_LogService>().As<ISys_LogService>().InstancePerLifetimeScope();
            containerBuilder.RegisterType<Sys_MenuService>().As<ISys_MenuService>().InstancePerLifetimeScope();
            containerBuilder.RegisterType<Sys_RoleNameService>().As<ISys_RoleNameService>().InstancePerLifetimeScope();
            containerBuilder.RegisterType<Sys_RoleMenuFunctionService>().As<ISys_RoleMenuFunctionService>().InstancePerLifetimeScope();
            containerBuilder.RegisterType<Sys_FunctionService>().As<ISys_FunctionService>().InstancePerLifetimeScope();



            ////注册类型仪
            //containerBuilder.RegisterInstance(typeFinder).As<ITypeFinder>().SingleInstance();
            ////查找由其他程序集提供的依赖性注册人
            //var dependencyRegistrars = typeFinder.FindClassesOfType<IDependencyRegistrar>();

            //创建和排序依赖性注册的实例
            //var instances = dependencyRegistrars
            //    //.Where(dependencyRegistrar => PluginManager.FindPlugin(dependencyRegistrar)?.Installed ?? true) //忽略未安装的插件
            //    .Select(dependencyRegistrar => (IDependencyRegistrar)Activator.CreateInstance(dependencyRegistrar))
            //    .OrderBy(dependencyRegistrar => dependencyRegistrar.Order);

            ////注册所有提供的依赖项
            //foreach (var dependencyRegistrar in instances)//容器
            //    dependencyRegistrar.Register(containerBuilder, typeFinder, nopConfig);

            //用一组注册的服务描述符填充Autofac容器构建器
            containerBuilder.Populate(services);

            //创建服务提供者
            _serviceProvider = new AutofacServiceProvider(containerBuilder.Build());
            return _serviceProvider;
        }

        ///// <summary>
        ///// Register and configure AutoMapper
        ///// </summary>
        ///// <param name="services">Collection of service descriptors</param>
        ///// <param name="typeFinder">Type finder</param>
        //protected virtual void AddAutoMapper(IServiceCollection services, ITypeFinder typeFinder)
        //{
        //    //find mapper configurations provided by other assemblies
        //    var mapperConfigurations = typeFinder.FindClassesOfType<IOrderedMapperProfile>();

        //    //create and sort instances of mapper configurations
        //    var instances = mapperConfigurations
        //        .Where(mapperConfiguration => PluginManager.FindPlugin(mapperConfiguration)?.Installed ?? true) //ignore not installed plugins
        //        .Select(mapperConfiguration => (IOrderedMapperProfile)Activator.CreateInstance(mapperConfiguration))
        //        .OrderBy(mapperConfiguration => mapperConfiguration.Order);

        //    //create AutoMapper configuration
        //    var config = new MapperConfiguration(cfg =>
        //    {
        //        foreach (var instance in instances)
        //        {
        //            cfg.AddProfile(instance.GetType());
        //        }
        //    });

        //    //register
        //    AutoMapperConfiguration.Init(config);
        //}

        #endregion

        #region 方法
        /// <summary>
        /// Initialize engine
        /// </summary>
        /// <param name="services">Collection of service descriptors</param>
        public void Initialize(IServiceCollection services)
        {
            //目前大多数APT供应商都需要TLS 1.2
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            var provider = services.BuildServiceProvider();
            var hostingEnvironment = provider.GetRequiredService<IHostingEnvironment>();
            var mvcCoreBuilder = services.AddMvcCore();

            //CommonHelper.DefaultFileProvider = new NopFileProvider(hostingEnvironment);
            ////initialize plugins
            //var nopConfig = provider.GetRequiredService<NopConfig>();
            //PluginManager.Initialize(mvcCoreBuilder.PartManager, nopConfig);
        }

        //private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        //{
        //    //check for assembly already loaded
        //    var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
        //    if (assembly != null)
        //        return assembly;

        //    //get assembly from TypeFinder
        //    var tf = Resolve<ITypeFinder>();
        //    assembly = tf.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
        //    return assembly;
        //}

        /// <summary>
        ///添加配置服务
        /// </summary>
        /// <param name="services">Collection of service descriptors</param>
        /// <param name="configuration">Configuration of the application</param>
        /// <returns>Service provider</returns>
        public IServiceProvider ConfigureServices(IServiceCollection services, IConfiguration configuration)
        {
            //services.AddOcelot().AddConsul();//添加ocelot服务

            //添加分布式缓存到内存
            services.AddDistributedMemoryCache();
            //session 注册
            services.AddSession(item =>
            {
                item.IdleTimeout = TimeSpan.FromMinutes(30);
                item.Cookie.HttpOnly = true;
                item.Cookie.IsEssential = true;
                item.Cookie.SameSite = SameSiteMode.None;
            });

            services.AddCors(options =>
            {
                options.AddPolicy("_myAllowSpecificOrigins",
                builder =>
                {
                    builder.WithOrigins("http://localhost:12406", "https://localhost:12407")// 
                           .AllowAnyHeader()
                           .WithMethods("GET", "POST")
                           .AllowCredentials();//指定处理cookie
                });
            });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            //数据库连接
            var testConfig = services.BuildServiceProvider().GetRequiredService<TestConfig>();
            services.AddDbContext<NopObjectContext>(options => options.UseLazyLoadingProxies().UseSqlServer(testConfig.SqlConnection));
            services.AddEntityFrameworkSqlServer();
            services.AddEntityFrameworkProxies();
            //ioc
            RegisterDependencies(services);

            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            //var instances = dependencyRegistrars
            //    .Select(dependencyRegistrar => (IDependencyRegistrar)Activator.CreateInstance(dependencyRegistrar))
            //    .OrderBy(dependencyRegistrar => dependencyRegistrar.Order);

            ////find startup configurations provided by other assemblies
            //var typeFinder = new WebAppTypeFinder();
            //var startupConfigurations = typeFinder.FindClassesOfType<INopStartup>();

            ////创建和排序启动配置的实例
            //var instances = startupConfigurations
            //    //.Where(startup => PluginManager.FindPlugin(startup)?.Installed ?? true) //ignore not installed plugins
            //    .Select(startup => (INopStartup)Activator.CreateInstance(startup))
            //    .OrderBy(startup => startup.Order);

            ////配置服务
            //foreach (var instance in instances)//容器
            //    instance.ConfigureServices(services, configuration);

            //注册映射器配置
            //AddAutoMapper(services, typeFinder);

            ////register dependencies
            ////注册依赖项
            //var nopConfig = services.BuildServiceProvider().GetService<NopConfig>();
            //RegisterDependencies(nopConfig, services, typeFinder);

            ////run startup tasks 运行启动任务
            //if (!nopConfig.IgnoreStartupTasks)
            //    RunStartupTasks(typeFinder);

            ////resolve assemblies here. otherwise, plugins can throw an exception when rendering views
            ////解析程序集。否则，插件在呈现视图时可能会抛出异常
            //AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

            return _serviceProvider;
        }

        /// <summary>
        /// Configure HTTP request pipeline
        /// </summary>
        /// <param name="application">Builder for configuring an application's request pipeline</param>
        public void ConfigureRequestPipeline(IApplicationBuilder application)
        {
            ////find startup configurations provided by other assemblies
            //var typeFinder = Resolve<ITypeFinder>();
            //var startupConfigurations = typeFinder.FindClassesOfType<INopStartup>();

            ////create and sort instances of startup configurations
            //var instances = startupConfigurations
            //    //.Where(startup => PluginManager.FindPlugin(startup)?.Installed ?? true) //ignore not installed plugins
            //    .Select(startup => (INopStartup)Activator.CreateInstance(startup))
            //    .OrderBy(startup => startup.Order);

            ////configure request pipeline
            //foreach (var instance in instances)
            //    instance.Configure(application);
        }

        /// <summary>
        /// Resolve dependency
        /// </summary>
        /// <typeparam name="T">Type of resolved service</typeparam>
        /// <returns>Resolved service</returns>
        public T Resolve<T>() where T : class
        {
            return (T)GetServiceProvider().GetRequiredService(typeof(T));
        }

        /// <summary>
        /// Resolve dependency
        /// </summary>
        /// <param name="type">Type of resolved service</param>
        /// <returns>Resolved service</returns>
        public object Resolve(Type type)
        {
            return GetServiceProvider().GetRequiredService(type);
        }

        /// <summary>
        /// Resolve dependencies
        /// </summary>
        /// <typeparam name="T">Type of resolved services</typeparam>
        /// <returns>Collection of resolved services</returns>
        public IEnumerable<T> ResolveAll<T>()
        {
            return (IEnumerable<T>)GetServiceProvider().GetServices(typeof(T));
        }

        /// <summary>
        /// Resolve unregistered service
        /// </summary>
        /// <param name="type">Type of service</param>
        /// <returns>Resolved service</returns>
        public virtual object ResolveUnregistered(Type type)
        {
            Exception innerException = null;
            foreach (var constructor in type.GetConstructors())
            {
                try
                {
                    //try to resolve constructor parameters
                    var parameters = constructor.GetParameters().Select(parameter =>
                    {
                        var service = Resolve(parameter.ParameterType);
                        if (service == null)
                            throw new Exception("Unknown dependency");
                        return service;
                    });

                    //all is ok, so create instance
                    return Activator.CreateInstance(type, parameters.ToArray());
                }
                catch (Exception ex)
                {
                    innerException = ex;
                }
            }

            throw new Exception("No constructor was found that had all the dependencies satisfied.", innerException);
        }
        #endregion
    }
}
